package simpleGeom;

import java.util.Vector;


public class TriangleMath {
	final static double SMALL_NUM = 0.0000000001f;
	HasPos3D p0;
	HasPos3D p1;
	HasPos3D p2;
	public Bounds3D b3d;
	public Vec3D n;
	Vec3D  u ;
	Vec3D  v;
	float   uu, uv, vv, wu, wv;
	public  TriangleMath(){
		
	}
	public  TriangleMath(HasPos3D p0,HasPos3D p1,HasPos3D p2){
		this.p0=p0;
		this.p1=p1;
		this.p2=p2;
		b3d=new Bounds3D();
		b3d.addPoint(p0);
		b3d.addPoint(p1);
		b3d.addPoint(p2);
		u = Vec3D.sub(p1, p0);
		v = Vec3D.sub(p2, p0);
		n=Vec3D.cross(u, v);
		n.normalize();
		uu = Vec3D.dot(u,u);
		uv = Vec3D.dot(u,v);
		vv = Vec3D.dot(v,v);
	}
	public Vec3D intersect_LineTriangle(HasPos3D r0,HasPos3D r1){
		  float     r, a, b;             // params to calc ray-plane intersect

		   Vec3D dir = Vec3D.sub(r1, r0);            // ray direction vector
		   Vec3D w0 =Vec3D.sub(r0, p0) ;
		    a = -Vec3D.dot(n,w0);
		    b = Vec3D.dot(n,dir);
		    if (Math.abs(b) < SMALL_NUM) {     // ray is parallel to triangle plane
		        if (a == 0)                // ray lies in triangle plane
		            return null;
		        else return null;             // ray disjoint from plane
		    }

		    // get intersect point of ray with triangle plane
		    r = a / b;
		    //if (r < 0.0)                   // ray goes away from triangle
		       // return null;                  // => no intersect
		    // for a segment, also test if (r > 1.0) => no intersect
		    Vec3D iP=Vec3D.add(r0,Vec3D.mult(dir, r));
		  //  *I = R.P0 + r * dir;           // intersect point of ray and plane

		    // is I inside T?
		    
		    
		    Vec3D w=Vec3D.sub(iP,p0);
		    
		    wu = Vec3D.dot(w,u);
		    wv =  Vec3D.dot(w,v);
		    float D = uv * uv - uu * vv;

		    // get and test parametric coords
		    float s, t;
		    s = (uv * wv - vv * wu) / D;
		    if (s < 0.0 || s > 1.0)        // I is outside T
		        return null;
		    t = (uv * wu - uu * wv) / D;
		    if (t < 0.0 || (s + t) > 1.0)  // I is outside T
		        return null;

		    return iP;                      // I is in T
	}
	
	final static float tol = 0.0001f;
	
	private  Vector<Intersection>getInterSectionsPts(HasPos3D a1, HasPos3D a2,
			HasPos3D a3,HasPos3D b1, HasPos3D b2, HasPos3D b3,boolean linear){
		HasPos3D[] tri = new HasPos3D[] { a1, a2, a3 };
		float[] dA = new float[3];
		Plane plane=new Plane(b1,b2,b3);
		for (int i = 0; i < tri.length; i++) {
			float dist = plane.getDistanceSigned(tri[i]);
			dA[i]=dist;
		}
		Vector<Intersection> intersectionPts = new Vector<Intersection>();
		for (int i = 0; i < 3; i++) {
			int i2 = (i + 1) % 3;
			float dist1 = dA[i];
			float dist2 = dA[i2];
			if (Math.abs(dist1) < tol) {
				if (Math.abs(dist2) < tol) {
					if (!linear){
						return null;
					}
					else{
						intersectionPts.add(new Intersection(tri[i], i, true));
						intersectionPts.add(new Intersection(tri[i2], i2, true));
						return intersectionPts;
					}
				} else {
					intersectionPts.add(new Intersection(tri[i], i, true));
				}
			} else {
				if ((dist1 < -tol && dist2 > tol)
						|| (dist1 > tol && dist2 < -tol)) {
					intersectionPts.add(new Intersection(plane
							.intersectWithRay(tri[i], tri[i2]), i, false));
				}
			}
		}
		return intersectionPts;
	}
	
	
	
	private  boolean doIntersect(HasPos3D a1,HasPos3D a2,HasPos3D b1,HasPos3D b2){
		// overlap of two lines
		float d = 0;
		float d1 = 0;
		float d2 = 0;
		float dX=a2.getX() - a1.getX();
		float dY=a2.getY() - a1.getY();
		float dZ=a2.getZ() - a1.getZ();
		int biggestDim=0;
		if (Math.abs(dY)>Math.abs(dX)&&Math.abs(dY)>Math.abs(dZ)){
			biggestDim=1;
		}
		if (Math.abs(dZ)>Math.abs(dX)&&Math.abs(dZ)>Math.abs(dY)){
			biggestDim=2;
		}
		if (biggestDim==0) {
			d = a2.getX() - a1.getX();
			d1 = b1.getX() - a1.getX();
			d2 = b2.getX() - a1.getX();
		} else if (biggestDim==1) {
			d = a2.getY() - a1.getY();
			d1 = b1.getY() - a1.getY();
			d2 = b2.getY() - a1.getY();
		} else if (biggestDim==2) {
			d = a2.getZ() - a1.getZ();
			d1 = b1.getZ() - a1.getZ();
			d2 = b2.getZ() - a1.getZ();
		}
		float t1 = d1 / d;
		float t2 = d2 / d;
		if (t2 < t1) {
			float tTemp=t1;
			t1=t2;
			t2=tTemp;
		}
		if (t2<-tol||t1>1+tol){
			return false;
		}
		return true;
	}
	
	public  Vector<SimpleTriangle> intersectTriPlane(HasPos3D a1, HasPos3D a2,
			HasPos3D a3, HasPos3D b1, HasPos3D b2, HasPos3D b3) {
		Vector<SimpleTriangle> tris = new Vector<SimpleTriangle>();
		HasPos3D[] tri = new HasPos3D[] { a1, a2, a3 };
		Plane plane = new Plane(b1, b2, b3);
		Vector<Intersection> iPts1 = getInterSectionsPts(a1,a2,a3,b1,b2,b3,false);
		if (iPts1==null)return tris;
		Vector<Intersection> iPts2 = getInterSectionsPts(b1,b2,b3,a1,a2,a3,true);
		if (iPts2==null)return tris;
		if (iPts1.size() != 2)
			return tris;
		if (iPts2.size() != 2)
			return tris;
		if (!doIntersect(iPts1.get(0).p,iPts1.get(1).p,iPts2.get(0).p,iPts2.get(1).p)){
			return tris;
		}
		Intersection i1 = iPts1.get(0);
		Intersection i2 = iPts1.get(1);
		if (i1.isNode) {
			tris.add(new SimpleTriangle(i1.p, tri[(i1.id + 1) % 3], i2.p));
			tris.add(new SimpleTriangle(i1.p, i2.p, tri[(i1.id + 2) % 3]));
		} else if (i2.isNode) {
			tris.add(new SimpleTriangle(i2.p, tri[(i2.id + 1) % 3], i1.p));
			tris.add(new SimpleTriangle(i2.p, i1.p, tri[(i2.id + 2) % 3]));
		} else {
			tris.add(new SimpleTriangle(i1.p, i2.p, tri[(i2.id + 1) % 3]));
			tris.add(new SimpleTriangle(i2.p, i1.p, tri[(i1.id + 1) % 3]));
			if (i1.id + 1 == i2.id) {
				tris.add(new SimpleTriangle(i1.p, tri[(i2.id + 1) % 3],
						tri[(i2.id + 2) % 3]));
			} else {
				tris.add(new SimpleTriangle(tri[(i1.id + 1) % 3],
						tri[(i1.id + 2) % 3], i2.p));
			}
		}
		return tris;
	}

	class Intersection {
		int id;
		boolean isNode;
		HasPos3D p;
		Intersection(HasPos3D p, int id, boolean isNode) {
			this.p = p;
			this.id = id;
			this.isNode = isNode;
		}
	}
	public Vector<SimpleTriangle> intersect(Mesh mesh,Vector<SimpleTriangle>triangles) {
		Vector<SimpleTriangle> tris = new Vector<SimpleTriangle>(triangles);
		for (int i = 0; i < tris.size(); i++) {
			SimpleTriangle tri = tris.get(i);
			tri.id = 0;
		}
		for (int j = 0; j < mesh.faces.size(); j++) {
			MeshFace face = mesh.getFace(j);
			for (int i = 0; i < tris.size(); i++) {
				SimpleTriangle tri = tris.get(i);
				if (tri.id == 0) {

					Vector<SimpleTriangle> cSplits = intersectTriPlane(
							tri.getNode(0), tri.getNode(1), tri.getNode(2),
							face.getNode(0), face.getNode(1), face.getNode(2));
					tris.addAll(cSplits);
					if (cSplits.size()>0){
							tri.id=10000;
					}
				}
			}
		}
		Vector<SimpleTriangle> finaltris = new Vector<SimpleTriangle>();
		for (int i = 0; i < tris.size(); i++) {
			SimpleTriangle tri = tris.get(i);
			if (tri.id == 0)
			finaltris.add(tri);
		}
		return finaltris;
	}
}
